using System;
using System.Collections.Generic;
using Assignment3.Entities;
using Assignment3.Objects;
using Assignment3.Skyboxes;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace Assignment3
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        private Camera _camera;
        private Skybox _skybox;
        private ReflectSphere _reflectSphere;
        private ReflectThingy _reflectThingy;
        private Effect bump;

        RasterizerState clockRazState;
        RasterizerState counterRazState;

        private List<Entity> _entities;
        private BasicEffect _basicEffect;

        private Matrix world = Matrix.Identity;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            _entities = new List<Entity>();
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.

            //effect = Content.Load<Effect>("Ambient");
            bump = Content.Load<Effect>("BumpMapped");
            spriteBatch = new SpriteBatch(GraphicsDevice);

            _basicEffect = LoadBasicEffect(new BasicEffect(GraphicsDevice));

            //Razterize state
            clockRazState = new RasterizerState();
            counterRazState = new RasterizerState();
            clockRazState.CullMode = CullMode.CullClockwiseFace;
            counterRazState.CullMode = CullMode.CullCounterClockwiseFace;

            _reflectSphere = new ReflectSphere(Content.Load<Model>("ReflectionSphere/UntexturedSphere"), Content.Load<Effect>("ReflectionSphere/Reflection"), Content.Load<TextureCube>("Skybox/Sunset"));
            _camera = new Camera(GraphicsDevice.Viewport, new Vector3(0,3,0), Vector3.Up );
            _skybox = new Skybox(Content.Load<Model>("Skybox/cube"), Content.Load<TextureCube>("Skybox/Sunset"), Content.Load<Effect>("Skybox/Skybox"));

            //jeep = Content.Load<Model>("Models/jeep");
            //jeepTexture = Content.Load<Texture2D>("Models/fbx/jeep-1");
            //sphereTexture = Content.Load<Texture2D>("Models/water-normal-map");
            //_sphere = Content.Load<Model>("Models/sphere_mapped");

            var car = new Car(_camera);
            car.Model = Content.Load<Model>("Models/jeep");
            car.Texture = Content.Load<Texture2D>("Models/fbx/jeep-1");
            car.Effects.Add("Basic", _basicEffect);
            car.Position = new Vector3(10, -5, 0);
            _entities.Add(car);

            car = new Car(_camera);
            car.Model = Content.Load<Model>("Models/jeep");
            car.Texture = Content.Load<Texture2D>("Models/fbx/jeep-1");
            car.Effects.Add("Basic", _basicEffect);
            car.Position = new Vector3(10, -5, 5);
            car.Rotation *= Matrix.CreateRotationY(180f);
            _entities.Add(car);

            var snow = new SnowPlow(_camera, GraphicsDevice);
            snow.Model = Content.Load<Model>("Models/snowplow");
            snow.Texture = Content.Load<Texture2D>("Models/leather");
            snow.Effects.Add("Basic", _basicEffect);
            snow.Position = new Vector3(-10, 0, 5);
            snow.Rotation *= Matrix.CreateRotationY(180f);
            _entities.Add(snow);

            var reflect = new ReflectThingy(_camera, GraphicsDevice, bump);
            reflect.Model = Content.Load<Model>("Models/sphere_mapped");
            reflect.Texture = Content.Load<Texture2D>("Models/water-normal-map");
            reflect.Position = new Vector3(0,0,25);
            _reflectThingy = reflect;
            //_entities.Add(reflect);

            var floor = new Floor(_camera, GraphicsDevice, Content.Load<Texture2D>("Models/setts-normalmap"));
            floor.Model = Content.Load<Model>("Models/plane");
            floor.Texture = Content.Load<Texture2D>("Models/setts-height");
            floor.Effects.Add("Bump", Content.Load<Effect>("BumpMapped"));
            floor.Effects.Add("Basic", _basicEffect);
            floor.Position = new Vector3(0, -5, -0);
            //_entities.Add(floor);
        }


        public BasicEffect LoadBasicEffect(BasicEffect effect)
        {
            effect.FogEnabled = true;
            effect.FogColor = Color.CornflowerBlue.ToVector3();
            effect.FogStart = 10f;
            effect.FogEnd = 100f;

            effect.LightingEnabled = true;

            effect.DirectionalLight0.DiffuseColor = new Vector3(0.2f, 0.2f, 0.2f);
            effect.DirectionalLight0.Direction = new Vector3(1, 0, 0);
            effect.DirectionalLight0.SpecularColor = new Vector3(0.3f, 0.3f, 0.3f);

            effect.AmbientLightColor = new Vector3(0.2f, 0.2f, 0.2f);
            effect.EmissiveColor = new Vector3(0.2f, 0.2f, 0.2f);

            return effect;
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
                this.Exit();

            // TODO: Add your update logic here
            _camera.Update(gameTime);

            foreach (var entity in _entities)
            {
                entity.Update(gameTime);
            }

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Purple);

            _basicEffect.View = _camera.View;
            _basicEffect.CurrentTechnique.Passes[0].Apply();

            Matrix temp = _camera.View;
            foreach (CubeMapFace mapFace in Enum.GetValues(typeof(CubeMapFace)))
            {
                _camera.View = _reflectThingy.ViewMatrix;

                _reflectThingy.CreateCubeMap(mapFace);
                GraphicsDevice.Clear(Color.CornflowerBlue);
                
                // do all draws
                GraphicsDevice.RasterizerState = clockRazState;
                _skybox.Draw(_camera.View, _reflectThingy.ProjectionMatrix, _reflectThingy.Position);
                GraphicsDevice.RasterizerState = counterRazState;

                GraphicsDevice.BlendState = BlendState.AlphaBlend;
                foreach (var entity in _entities)
                {
                    entity.Draw();
                }
                GraphicsDevice.BlendState = BlendState.Opaque;
                // ends it
                _reflectThingy.EndCubeMap();
                //_camera.View = temp;
            }
            _camera.View = temp;


            GraphicsDevice.RasterizerState = clockRazState;
            _skybox.Draw(_camera.View, _camera.Projection, _camera.Position);
            GraphicsDevice.RasterizerState = counterRazState;

            _reflectThingy.Draw();
            


            foreach (var entity in _entities)
            {
                entity.Draw();
            }
           

            base.Draw(gameTime);
        }
    }
}
